//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_dao

import (
	"context"
	"regexp"
	"time"

	"atomgit.com/openlinksaas/api-server/dao/db_ns"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type BasicProjectInfoDbItem struct {
	ProjectName string `bson:"projectName"`
	ProjectDesc string `bson:"projectDesc"`
}

type SettingDbItem struct {
	// DisableMemberAppraise bool `bson:"disableMemberAppraise"`
	// DisableTestCase       bool   `bson:"disableTestCase"`
	// DisableSprit         bool `bson:"disableSprit"`
	// DisableServerAgent   bool `bson:"disableServerAgent"`
	// DisableExtEvent bool `bson:"disableExtEvent"`
	// DisableAppStore      bool `bson:"disableAppStore"`
	// DisableDataAnno      bool `bson:"disableDataAnno"`
	// DisableApiCollection bool `bson:"disableApiCollection"`
	// DisableCodeComment   bool `bson:"disableCodeComment"`
	// DisableCiCd bool `bson:"disableCiCd"`
	// MainContent uint32 `bson:"mainContent"`

	// DisableChat     bool `bson:"disableChat"`
	// DisableKb       bool `bson:"disableKb"`
	// DisableWorkPlan bool `bson:"disableWorkPlan"`

	//沟通相关配置
	// MinPureTextLenInChat uint32 `bson:"minPureTextLenInChat"`
	// DisableWidgetInChat  bool   `bson:"disableWidgetInChat"`

	//自定义事件相关
	HideCustomEvent         bool `bson:"hideCustomEvent"`
	HideCustomEventForAdmin bool `bson:"hideCustomEventForAdmin"`

	// 项目概览页面相关
	// HideProjectInfo bool `bson:"hideProjectInfo"`
	// HideBulletin    bool `bson:"hideBulletin"`
	// HideExtraInfo   bool `bson:"hideExtraInfo"`
	// HideWatchDoc      bool `bson:"hideWatchDoc"`
	// HideWatchWalkPlan bool `bson:"hideWatchWalkPlan"`
	// HideWatchTask bool `bson:"hideWatchTask"`
	// HideWatchBug  bool `bson:"hideWatchBug"`
	// HideWatchChannel  bool `bson:"hideWatchChannel"`
	// K8SProxyAddr   string `bson:"k8SProxyAddr"`
	// SwarmProxyAddr string `bson:"wwarmProxyAddr"`
	// TraceProxyAddr string `bson:"traceProxyAddr"`
	// NetProxyAddr   string `bson:"netProxyAddr"`
}

type ProjectInfoDbItem struct {
	ProjectId   string                 `bson:"_id"`
	BasicInfo   BasicProjectInfoDbItem `bson:"basicInfo"`
	CreateTime  int64                  `bson:"createTime"`
	UpdateTime  int64                  `bson:"updateTime"`
	Closed      bool                   `bson:"closed"`
	OwnerUserId string                 `bson:"ownerUserId"`
	// DefaultChannelId  string                 `bson:"defaultChannelId"`
	DefaultRoleId string `bson:"defaultRoleId"`
	// DefaultDocSpaceId string `bson:"defaultDocSpaceId"`
	// ChannelFsId       string                 `bson:"channelFsId"`
	ProjectFsId string `bson:"projectFsId"`
	IssueFsId   string `bson:"issueFsId"`
	DocFsId     string `bson:"docFsId"`
	// EbookFsId         string                 `bson:"ebookFsId"`
	// ArtifactFsId      string                 `bson:"artifactFsId"`
	// MinAppFsId      string        `bson:"minAppFsId"`
	RequireMentFsId string `bson:"requireMentFsId"`
	IdeaFsId        string `bson:"ideaFsId"`
	BulletinFsId    string `bson:"bulletinFsId"`
	ApiCollFsId     string `bson:"apiCollFsId"`
	DataAnnoFsId    string `bson:"dataAnnoFsId"`
	// CiCdFsId        string        `bson:"ciCdFsId"`
	PagesFsId          string        `bson:"pagesFsId"`
	BoardFsId          string        `bson:"boardFsId"`
	FileFsId           string        `bson:"fileFsId"`
	TestCaseFsId       string        `bson:"testCaseFsId"`
	TestResultFsId     string        `bson:"testResultFsId"`
	DefaultChatGroupId string        `bson:"defaultChatGroupId"`
	DefaultIdeaGroupId string        `bson:"defaultIdeaGroupId"`
	Setting            SettingDbItem `bson:"setting"`
	TipList            []string      `bson:"tipList"`
}

func (item *ProjectInfoDbItem) ToProjectInfo(userProjectPerm *project_api.UserProjectPerm) *project_api.ProjectInfo {
	tipList := []string{}
	if item.TipList != nil {
		tipList = item.TipList
	}
	return &project_api.ProjectInfo{
		ProjectId: item.ProjectId,
		BasicInfo: &project_api.BasicProjectInfo{
			ProjectName: item.BasicInfo.ProjectName,
			ProjectDesc: item.BasicInfo.ProjectDesc,
		},
		CreateTime:         item.CreateTime,
		UpdateTime:         item.UpdateTime,
		Closed:             item.Closed,
		OwnerUserId:        item.OwnerUserId,
		DefaultRoleId:      item.DefaultRoleId,
		IssueFsId:          item.IssueFsId,
		ProjectFsId:        item.ProjectFsId,
		DocFsId:            item.DocFsId,
		RequireMentFsId:    item.RequireMentFsId,
		IdeaFsId:           item.IdeaFsId,
		BulletinFsId:       item.BulletinFsId,
		ApiCollFsId:        item.ApiCollFsId,
		DataAnnoFsId:       item.DataAnnoFsId,
		PagesFsId:          item.PagesFsId,
		BoardFsId:          item.BoardFsId,
		FileFsId:           item.FileFsId,
		TestCaseFsId:       item.TestCaseFsId,
		TestResultFsId:     item.TestResultFsId,
		DefaultChatGroupId: item.DefaultChatGroupId,
		DefaultIdeaGroupId: item.DefaultIdeaGroupId,
		UserProjectPerm:    userProjectPerm,
		Setting: &project_api.Setting{
			HideCustomEvent:         item.Setting.HideCustomEvent,
			HideCustomEventForAdmin: item.Setting.HideCustomEventForAdmin,
		},
		TipList: tipList,
	}
}

type ListProjectByAdminApiParam struct {
	FilterClosed      bool
	Closed            bool
	FilterByKeyword   bool
	Keyword           string
	FilterByRemove    bool
	Remove            bool
	FilterByProjectId bool
	ProjectIdList     []string
}

func (param *ListProjectByAdminApiParam) genQuery() bson.M {
	query := bson.M{}
	if param.FilterClosed {
		query["closed"] = param.Closed
	}
	if param.FilterByKeyword {
		query["basicInfo.projectName"] = primitive.Regex{
			Pattern: regexp.QuoteMeta(param.Keyword),
			Options: "i",
		}
	}
	if param.FilterByRemove {
		if param.Remove {
			query["ownerUserId"] = ""
		} else {
			query["ownerUserId"] = bson.M{"$ne": ""}
		}
	}
	if param.FilterByProjectId {
		query["_id"] = bson.M{"$in": param.ProjectIdList}
	}
	return query
}

type _ProjectInfoDao struct {
	mc *mongo.Collection
}

func newProjectInfoDao(db *mongo.Database) (*_ProjectInfoDao, error) {
	mc := db.Collection(db_ns.DB_NS_PROJECT + "info")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"createTime", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_ProjectInfoDao{
		mc: mc,
	}, nil
}

func (db *_ProjectInfoDao) Insert(ctx context.Context, dbItem *ProjectInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_ProjectInfoDao) Exist(ctx context.Context, projectId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": projectId})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_ProjectInfoDao) Get(ctx context.Context, projectId string) (*ProjectInfoDbItem, error) {
	retItem := &ProjectInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": projectId}).Decode(retItem)
	return retItem, err
}

func (db *_ProjectInfoDao) UpdateBasicInfo(ctx context.Context, projectId string, basicItem *BasicProjectInfoDbItem) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"basicInfo": basicItem, "updateTime": time.Now().UnixNano() / 1e6}})
	return err
}

func (db *_ProjectInfoDao) UpdateSetting(ctx context.Context, projectId string, setting *SettingDbItem) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"setting": setting, "updateTime": time.Now().UnixNano() / 1e6}})
	return err
}

func (db *_ProjectInfoDao) UpdateTipList(ctx context.Context, projectId string, tipList []string) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"tipList": tipList, "updateTime": time.Now().UnixNano() / 1e6}})
	return err
}

func (db *_ProjectInfoDao) ListById(ctx context.Context, projectIdList []string, filterClosed, closed bool) ([]*ProjectInfoDbItem, error) {
	query := bson.M{"_id": bson.M{"$in": projectIdList}}
	if filterClosed {
		query["closed"] = closed
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(bson.M{"updateTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*ProjectInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_ProjectInfoDao) CountAll(ctx context.Context) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{})
	return uint32(count), err
}

func (db *_ProjectInfoDao) ListAll(ctx context.Context, offset, limit int64) ([]*ProjectInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{}, options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*ProjectInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_ProjectInfoDao) CountByAdminApi(ctx context.Context, param *ListProjectByAdminApiParam) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, param.genQuery())
	return uint32(count), err
}

func (db *_ProjectInfoDao) ListByAdminApi(ctx context.Context, param *ListProjectByAdminApiParam, offset, limit int64) ([]*ProjectInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, param.genQuery(), options.Find().SetSkip(offset).SetLimit(limit).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*ProjectInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_ProjectInfoDao) ClearOwner(ctx context.Context, projectId string) error {
	nowTime := time.Now().UnixNano() / 1e6
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"ownerUserId": "", "updateTime": nowTime}})
	return err
}

func (db *_ProjectInfoDao) UpdateCloseFlag(ctx context.Context, projectId string, closeFlag bool) error {
	nowTime := time.Now().UnixNano() / 1e6
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"closed": closeFlag, "updateTime": nowTime}})
	return err
}

func (db *_ProjectInfoDao) UpdateProject(ctx context.Context, dbItem *ProjectInfoDbItem) error {
	_, err := db.mc.UpdateByID(ctx, dbItem.ProjectId, bson.M{"$set": dbItem})
	return err
}

func (db *_ProjectInfoDao) SetOwner(ctx context.Context, projectId, ownerUserId string, nowTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": projectId}, bson.M{"$set": bson.M{"ownerUserId": ownerUserId, "updateTime": nowTime}})
	return err
}
